module sw.trans;
/**
 * パースしたのからコマンドへの変換
 */
import sw.commands, sw.parser, std.stdio, std.string, std.regexp, nazo.xhtml, core.memory, std.contracts, sw.siteconfig, nazo.http;

///深い方から展開していく。
///スタックオーバーフローに注意。
static struct Translator{
/+  static string toXhtml2(SiteConfig* cfg,string source,uint hierarchy=1,string path=Http.relPath){
    size_t i=0;
    return complex(cfg, Escape.arg(source), hierarchy, path, i);
  }
  private static string cmd(SiteConfig* cfg,string source,uint hierarchy,string path, ref size_t i){
    string name;
    string[] args;

    mainloop:for(;i<source.length;i++){
      switch(source[i]){
        case ',':
          Cmd* cmd;
          if(text.length){
            text~=source[ib..i];
            base.args[$-1]~=newTextCmd(text);
            text="";
          }else if(ib!=i)
            base.args[$-1]~=newTextCmd(source[ib..i]);
          ib=i+1;
          base.args.length=base.args.length+1;
          break;
        case '[':
          if(text.length){
            text~=source[ib..i];
            base.args[$-1]~=newTextCmd(text);
            text="";
          }else if(ib!=i)
            base.args[$-1]~=newTextCmd(source[ib..i]);
          ib=i+1;
          Cmd* cmd=newCmd;
          cmd.parent=base;
          base.args[$-1]~=cmd;
          base=cmd;
          i++;
          goto titleloop;
        case ']':
          if(text.length){
            text~=source[ib..i];
            base.args[$-1]~=newTextCmd(text);
            text="";
          }else
            if(/*source[ib..i].length!=0*/ib!=i)
              base.args[$-1]~=newTextCmd(source[ib..i]);
          ib=i+1;
          base=base.parent;
          break;
        case '\\':
          if(!text.length)text=source[ib..i];
          else text~=source[ib..i];
          ib=i+1;
          i++;
          break;
        default:
      }
    }
    titleloop:for(;i<source.length;i++){
      switch(source[i]){
        case '\r':
          if(base.name.length)
            base.name~=source[ib..i];
          else
            base.name=source[ib..i];
          ib=i+1;
          break;
        case ' ':
        case '\n':
          base.args.length=1;
          if(base.name.length)
            base.name~=source[ib..i];
          else
            base.name=source[ib..i];
          ib=i+1;
          i++;
          goto mainloop;
        case ']':
          if(base.name.length)
            base.name~=source[ib..i];
          else
            base.name=source[ib..i];
          ib=i+1;
          base=base.parent;
          i++;
          goto mainloop;
        case '\\':
          if(base.name.length)
            base.name~=source[ib..i];
          else
            base.name=source[ib..i];
          ib=i+1;
          i++;
          break;
        default:
      }
    }
  }
  private static string complex(SiteConfig* cfg,string source,uint hierarchy,string path, ref size_t i){
    string rv;
    size_t bi;

    for(;i<source.length;i++){
      switch(source[i]){
        case '[':
          if(rv.length)rv~=source[bi..i];
          else         rv=source[bi..i];
          if(rv.length)rv~=cmd(cfg, source, hierarchy, path, ++i);
          else         rv=cmd(cfg, source, hierarchy, path, ++i);
          bi = i;
          break;
        case ',':
        case ']':
          if(rv.length)rv~=source[bi..i];
          else         rv=source[bi..i];
          return rv;
        case '\\':
          if(rv.length)rv~=source[bi..i];
          else         rv=source[bi..i];
          bi = i++ + 1;
          break;
        default:
          break;
      }
    }
    if(rv.length)rv~=source[bi..i];
    else         rv=source[bi..i];
    return rv;
  }+/
  static string toXhtml(SiteConfig* cfg,string source,uint hierarchy=1,string path=Http.relPath){
//    GC.disable;
    string tmp=trans(cfg,hierarchy,Parser.parse(Escape.arg(source)),path);
//    GC.enable;
    return tmp;
  }
  private static string trans(SiteConfig* cfg,uint hierarchy, Cmd*[] cmds,string path){
    assert(cmds.length>0);
    if(cmds.length==1){
      if(!cmds[0].parent)
        return cmds[0].text;
      scope string[] args=new string[cmds[0].args.length];
      foreach(uint i,inout Cmd*[] arg;cmds[0].args)
        args[i]=trans(cfg,hierarchy,arg,path);
      int n=cmds[0].name.find(':');
      Commands command=CommandsList.getCommandsList(n==-1?"":cmds[0].name[0..n]);
      return command.value(cfg,cmds[0].name[n+1..$],args,cmds[0],hierarchy,path);
    }

    scope string[] value=new string[cmds.length];
    foreach(uint index,inout Cmd* cmd;cmds){
      if(!cmd.parent){
        value[index]=cmd.text;
        continue;
      }
      scope string[] args=new string[cmd.args.length];
      foreach(uint i,inout Cmd*[] arg;cmd.args)
        args[i]=trans(cfg,hierarchy,arg,path);
      int n=cmd.name.find(':');
      Commands command=CommandsList.getCommandsList(n==-1?"":cmd.name[0..n]);
      value[index]=command.value(cfg,cmd.name[n+1..$],args,cmd,hierarchy,path);
    }
    return nazo.string.join(value);
  }
}

///FIXME: unittestが適当
/*unittest{
  writef("\n\n");
  writef(Translator.toXhtml("[sec test,[p [l okok][l okok]][sec test,[p [l okok][l okok]]]]"));
  throw new Exception("中断");
}*/
